package com.geek.framework.study.config.anno;

import com.geek.framework.study.common.BaseException;
import com.geek.framework.study.common.EncodedResource;
import com.geek.framework.study.common.ThrowableSupplier;
import com.geek.framework.study.config.support.ConfigSourceFactory;
import com.geek.framework.study.config.support.DefaultConfigSourceFactory;
import org.eclipse.microprofile.config.spi.ConfigSource;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * @see ConfigSourceFactory
 */
public class ResourceConfigSourcesParser {

    private static final ConfigSourceFactory DEFAULT_CONFIG_SOURCE_FACTORY = new DefaultConfigSourceFactory();


    private List<Class<?>> objects = new ArrayList<>();

    private final AtomicBoolean hasLoaded = new AtomicBoolean(false);

    /**
     * 将注册的对象中的{@link ResourceConfigSource} 和 {@link ResourceConfigSources} 进行解析
     */
    public List<ConfigSource> parse() {
        if (hasLoaded.get()) {
            throw new BaseException("已经被加载过了, 不支持重复加载");
        }
        hasLoaded.set(true);
        List<ConfigSource> configSourceList = new ArrayList<>();
        for (Class<?> clazz : objects) {
            ResourceConfigSource configSource = clazz.getAnnotation(ResourceConfigSource.class);
            addConfigSourceIfNotNull(configSourceList, configSource);

            ResourceConfigSources configSources = clazz.getAnnotation(ResourceConfigSources.class);
            addConfigSourcesIfNotNull(configSourceList, configSources);
        }
        return configSourceList;
    }

    private void addConfigSourcesIfNotNull(List<ConfigSource> configSourceList, ResourceConfigSources configSources) {
        if (configSources == null) {
            return ;
        }
        for (ResourceConfigSource configSource : configSources.value()) {
            addConfigSourceIfNotNull(configSourceList, configSource);
        }
    }


    private void addConfigSourceIfNotNull(List<ConfigSource> configSourceList, ResourceConfigSource configSource) {
        if (configSource == null) {
            return;
        }
        ConfigSource source = processConfigSource(configSource);
        if (source == null) {
            return;
        }
        configSourceList.add(source);
    }

    private ConfigSource processConfigSource(ResourceConfigSource configSource) {
        try {
            String encoding = configSource.encoding();
            String[] value = configSource.value();
            String name = configSource.name();
            int ordinal = configSource.ordinal();
            boolean b = configSource.ignoreResourceNotFound();
            Class<? extends ConfigSourceFactory> factory = configSource.factory();
            ConfigSourceFactory sourceFactory = factory == ConfigSourceFactory.class ? DEFAULT_CONFIG_SOURCE_FACTORY :
                    ThrowableSupplier.execute(() -> factory.newInstance());
            List<EncodedResource> encodedResources = new ArrayList<>();
            for (String path : value) {
                encodedResources.add(new EncodedResource(encoding, new URL(path)));
            }
            return sourceFactory.createConfigSource(name, ordinal, b, encodedResources);
        } catch (Exception e) {
            return null;
        }
    }



    public void register(Class<?> clazz) {
        objects.add(clazz);
    }


}
